import { Container } from "../Container";
import { HelperTool } from "./Common";
import { DataService } from "./DataService";
import { GroupEntity, GroupManService } from "./GroupManService";
import { ViewerService } from "./ViewerService";


export class AnimationService {
    static CreateLinear(previousframe: { time: number, data: any }, nextFrame: { time: number, data: any }, currentFrame: { time: number, data: any }) {
        let difPer = (currentFrame.time - previousframe.time) / (nextFrame.time - previousframe.time);
        for (let name in currentFrame.data) {
            if (typeof currentFrame.data[name] == 'number'
                && typeof nextFrame.data[name] == 'number'
                && typeof previousframe.data[name] == 'number') {
                currentFrame.data[name] = (nextFrame.data[name] - previousframe.data[name]) * difPer + previousframe.data[name];
            }
            else if (typeof currentFrame.data[name] == 'object'
                && typeof nextFrame.data[name] == 'object'
                && typeof previousframe.data[name] == 'object')
                this.CreateLinear({ time: previousframe.time, data: previousframe.data[name] },
                    { time: nextFrame.time, data: nextFrame.data[name] },
                    { time: currentFrame.time, data: currentFrame.data[name] })
            else{
                //console.warn("数据不一致");
            }
        }
    }
    static CreateFrame(animation: Animation, time: number): AnimationFrame {
        let nextFrameIndex = animation.frames.findIndex(_ => _.time >= time);
        if (nextFrameIndex < 0)
            throw new Error("time is not in animation time range.");
        let nextFrame = animation.frames[nextFrameIndex];
        if (nextFrame.time == time)
            return nextFrame;
        let previousframe = animation.frames[nextFrameIndex - 1];

        let currentState = HelperTool.Clone(nextFrame.state);
        let prevState = previousframe.state;
        let nextState = nextFrame.state;

        try {
            this.CreateLinear({ time: previousframe.time, data: prevState }
                , { time: nextFrame.time, data: nextState }
                , { time: time, data: currentState });
        }
        catch (e) {
            console.error([previousframe, nextFrame, time])
        }

        return { time, state: currentState };
    }
    private DataService = Container.defineProperty(DataService);
    private ViewerService = Container.defineProperty(ViewerService);
    private GroupManService = Container.defineProperty(GroupManService);
    getData(): Animation[] {
        if (!this.DataService.Data.animations) {
            this.DataService.Data.animations = [];
        }
        this.checkData(this.DataService.Data.animations);
        return this.DataService.Data.animations;
    }
    private checkData(values: Animation[]) {
        values.forEach(_ => {
            if (_.frames.length > 0 && _.frames[0].object && !_.object) {
                _.object = _.frames[0].object;
                _.frames.forEach(item => delete item.object);
            }
        });
    }

    CreateCameraAnimation(): Animation {
        return { name: "", object: undefined, frames: [] };
    }
    CreateGroupAnimation(value: GroupEntity): Animation {
        return { name: "", object: { type: "group", name: value.name }, frames: [] };
    }
    CreateFrame(value: Animation) {
        if (value.object && value.object.type == "group") {
            let group = this.GroupManService.getData().groups.find(_ => _.name == value.object.name);
            return { state: this.GetGroupState(group!), time: 0 };
        }
        else {
            return { state: this.GetCameraState(this.ViewerService.viewer), time: 0 };
        }
    }
    SetFrame(animation: Animation, time: number) {
        let value = AnimationService.CreateFrame(animation, time);
        if (animation.object && animation.object.type == "group") {
            let temp = this.GroupManService.getData().groups.find(_ => _.name == animation.object.name);
            if (temp) {
                this.SetGroupState(temp!, value.state);
                this.GroupManService.updateViewer();
            }
        }
        else {
            this.SetCameraState(this.ViewerService.viewer, value.state);
        }
    }

    private GetCameraState(viewer: Sippreep.Viewing.Viewer3D) {
        const c = viewer.navigation.getCamera();
        return {
            viewport: { eye: c.position.toArray(), target: c.target.toArray() }
        };
    }
    private SetCameraState(viewer: Sippreep.Viewing.Viewer3D, state: any) {
        const c = viewer.navigation.getCamera();
        const position = new THREE.Vector3().fromArray(state.viewport.eye);
        const target = new THREE.Vector3().fromArray(state.viewport.target);
        if (position.distanceToSquared(c.position) !== 0 || target.distanceToSquared(c.target) !== 0) {
            viewer.navigation.setView(position, target);
            viewer.navigation.orientCameraUp();
        }
    }
    private GetGroupState(value: GroupEntity) {
        return {
            disableColor: value.disableColor,
            color: value.color,
            transform: value.transform || { center: [0, 0, 0], position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1] }
        }
    }
    private SetGroupState(value: GroupEntity, state: any) {
        if (state.transform != undefined)
            value.transform = state.transform;
        if (state.disableColor != undefined)
            value.disableColor = state.disableColor;
        if (state.color != undefined)
            value.color = state.color;
    }
}
export type Animation = { name: string, object: any, time?: number, frames: AnimationFrame[] };
export type AnimationFrame = {
    time: number,
    state: any,
    object?: any,
    name?: string,
};
